/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package shaman.storage.impl.file;

import com.thoughtworks.xstream.XStream;
import java.awt.Frame;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import shaman.storage.api.Backup;
import shaman.storage.api.Filter;
import shaman.storage.api.Storage;
import shaman.storage.impl.util.ObjectStreamIterable;

/**
 *
 * @author dmitriy
 */
public abstract class FileStorage implements Storage {

    public static final String PROPERTY_PART_SIZE_LIMIT="Размер тома";
    public static final String PROPERTY_BLOCK_SIZE_LIMIT="Интервал непрерывности";
    public static final String BACKUP_LIST_FILE="backups.xml";

    public static final List<String> propertyNames=Collections.unmodifiableList(Arrays.asList(new String[] {
        PROPERTY_PART_SIZE_LIMIT,
        PROPERTY_BLOCK_SIZE_LIMIT
    }));

    private String comment;
    private long partSizeLimit=0x40000000;
    private long blockSizeLimit=0x1000000;

    private class StorageAccessor implements StorageFileAccessor {

        public StorageAccessor() {
        }

        public InputStream fetchFile(String name, long offset, long length) throws FileNotFoundException, IOException {
            return getStorageFile(name, offset, length);
        }

        public OutputStream storeFile(String name) throws FileNotFoundException, IOException {
            return writeStorageFile(name);
        }
        
    }

    private class FinishListener implements BackupFinishListener {

        public FinishListener() {
        }

        public void finishBackup(Date date) throws IOException {
            List<Date> dates=loadList();
            dates.add(date);
            saveList(dates);
        }

    }

    public FileStorage() {
    }

    public String getTypeName() {
        return "Файловое";
    }

    public abstract String getLocation();

    public abstract InputStream getStorageFile(String name, long offset, long length) throws IOException, FileNotFoundException;

    public abstract OutputStream writeStorageFile(String name) throws IOException, FileNotFoundException;

    public String getComment() {
        return comment;
    }

    public void setComment(String text) {
        comment=text;
    }

    public List<Backup> getBackups() throws IOException {
        List<Backup> backups=new ArrayList<Backup>();
        for (Date date: loadList())
        {
            backups.add(new FileBackup(new StorageAccessor(), null, date, null));
        }
        return backups;
    }

    public Backup newBackup(List<Filter> filters) throws IOException {
        FileBackupMetadata md=new FileBackupMetadata();
        List<Filter> preprocessors=new ArrayList<Filter>();
        List<Filter> mainFilters=new ArrayList<Filter>();
        List<Filter> postprocessors=new ArrayList<Filter>();
        boolean preproc=true;
        for (Filter filt: filters) {
            Filter.FilterType type=filt.getFilterType();
            if (preproc && type==Filter.FilterType.PREPROCESSOR)
                preprocessors.add(filt);
            else {
                postprocessors.add(filt);
                preproc=false;
            }
            if (type!=Filter.FilterType.POSTPROCESSOR)
            {
                mainFilters.addAll(postprocessors);
                postprocessors.clear();
            }
        }
        md.blockSizeLimit=blockSizeLimit;
        md.partSizeLimit=partSizeLimit;
        md.preprocessors=preprocessors;
        md.mainFilters=mainFilters;
        md.postprocessors=postprocessors;
        return new FileBackup(new StorageAccessor(), md, new Date(), new FinishListener());
    }

    public boolean hasConfigForm() {
        return false;
    }

    public boolean showConfigForm(Frame parent) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public List<String> getPropertyNames() {
        return propertyNames;
    }

    public String getPropertyValue(String name) throws InvalidParameterException {
        if (PROPERTY_BLOCK_SIZE_LIMIT.equals(name))
            return String.valueOf(blockSizeLimit);
        else if (PROPERTY_PART_SIZE_LIMIT.equals(name))
            return String.valueOf(partSizeLimit);
        else
            throw new InvalidParameterException("Unknown property: "+name);
    }

    public List<String> getPropertyValues(String name) throws InvalidParameterException {
        if (!propertyNames.contains(name))
            throw new InvalidParameterException("Unknown property: "+name);
        return null;
    }

    public void setPropertyValue(String name, String value) throws InvalidParameterException {
        if (!propertyNames.contains(name))
            throw new InvalidParameterException("Unknown property: "+name);
        long numericValue;
        try {
            numericValue=Long.parseLong(value);
        } catch (NumberFormatException ex) {
            throw new InvalidParameterException("Incorrect value for property: "+name);
        }
        if (PROPERTY_BLOCK_SIZE_LIMIT.equals(name))
            blockSizeLimit=numericValue;
        if (PROPERTY_PART_SIZE_LIMIT.equals(name))
            partSizeLimit=numericValue;
    }

    protected List<Date> loadList() throws IOException
    {
        List<Date> ret=new ArrayList<Date>();
        InputStream is;
        try {
            is=getStorageFile(BACKUP_LIST_FILE, 0, -1);
        } catch (FileNotFoundException ex) {
            return ret;
        }
        ObjectInputStream ois;
        try {
            XStream xs=new XStream();
            ois=xs.createObjectInputStream(is);
        } catch (Throwable t) {
            if (t instanceof IOException)
                throw (IOException) t;
            else
                throw new IterationException(t);
        }
        try {
            for (Long date: new ObjectStreamIterable<Long>(ois, Long.class))
            {
                ret.add(new Date(date));
            }
        } finally {
            try {
                ois.close();
            } finally {
                is.close();
            }
        }
        return ret;
    }

    protected void saveList(List<Date> list) throws IOException
    {
        OutputStream os;
        os=writeStorageFile(BACKUP_LIST_FILE);
        ObjectOutputStream oos;
        try {
            XStream xs=new XStream();
            oos=xs.createObjectOutputStream(os, "backups");
        } catch (Throwable t) {
            if (t instanceof IOException)
                throw (IOException) t;
            else
                throw new IterationException(t);
        }
        try {
            for (Date date: list)
            {
                oos.writeObject(date.getTime());
            }
        } finally {
            try {
                oos.close();
            } finally {
                os.close();
            }
        }
    }
}
